import constants from '@/constants';
import {
  ArgumentsHost,
  BadRequestException,
  Catch,
  ExceptionFilter,
  HttpException,
  HttpStatus,
} from '@nestjs/common';
import { HttpAdapterHost } from '@nestjs/core';
import { Prisma } from '@prisma/client';

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  constructor(private readonly httpAdapterHost: HttpAdapterHost) {}

  catch(exception: unknown, host: ArgumentsHost): void {
    if (!(exception instanceof HttpException)) {
      console.error(exception);
    }
    // In certain situations `httpAdapter` might not be available in the
    // constructor method, thus we should resolve it here.
    const { httpAdapter } = this.httpAdapterHost;

    const ctx = host.switchToHttp();
    const method = ctx.getRequest().method;
    const path = ctx.getRequest().url;

    let statusCode =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.INTERNAL_SERVER_ERROR;

    let code = statusCode;
    let message = '服务器内部错误';

    if (exception instanceof Error) {
      message = exception.message;
    }

    if (method !== 'HEAD') {
      console.error(code, path, message);
    }

    if (exception instanceof HttpException) {
      const response = exception.getResponse() as any;
      if (exception instanceof BadRequestException) {
        message = '请求成功，参数错误';
        statusCode = HttpStatus.OK;
        if (typeof response === 'string') {
          message = response;
        }
        if (typeof response === 'object') {
          const errors = (response as any).message ?? [];
          if (errors.length > 0) {
            message = errors[0];
          }
        }
      }
    } else if (exception instanceof Prisma.PrismaClientKnownRequestError) {
      code = HttpStatus.OK;
      message = `${exception.code} - ${exception.meta?.cause ?? exception.message}`;
    } else {
      if (!constants.isDev) {
        code = HttpStatus.OK;
        message = '内部错误';
      }
    }

    httpAdapter.reply(
      ctx.getResponse(),
      {
        code,
        success: false,
        message,
        data: null,
        path,
      },
      HttpStatus.OK,
    );
  }
}
